import "ol/ol.css";
import { Map, View, Feature } from "ol/index";
import {
  Tile as TileLayers,
  Graticule,
  Vector as VectorLayer,
  Heatmap as HeatmapLayer,
  WebGLPoints as WebGLPointsLayer,
} from "ol/layer";
import { XYZ, Vector as VectorSource } from "ol/source";
import OSM from "ol/source/OSM.js";
import TileLayer from "ol/layer/Tile.js";
import * as control from "ol/control";

import { boundingExtent, containsExtent, getCenter } from "ol/extent";
import { Circle as CircleStyle, Fill, Stroke, Style, Icon } from "ol/style";
import { Circle, LineString, Polygon } from "ol/geom";

import { easeIn, easeOut } from "ol/easing";

import GeoJSON from "ol/format/GeoJSON";

import Overlay from "ol/Overlay";

import { Point } from "ol/geom";

import cam_icon0 from "../img/03.svg";

// import { BaiduMap } from "@supermap/iclient-ol";
let map;
let overlay; //弹窗覆盖层
var allLayers = {}; // 存放所有图层
const defaultCenter = [114, 23]; //广州坐标点
// const defaultCenter = [106.5, 29.5]; //重庆坐标点
const defaultZoom = 5;
const defaultRotate = 0;
let view = new View({
  center: defaultCenter,
  maxZoom: 10,
  zoom: defaultZoom,
  minZoom: 2,
  rotate: defaultRotate,
  projection: "EPSG:4326",
});

/**
 * @use 初始OSM化地图
 * @param mapDom 地图容器目标id
 */
export const initOsmMap = (mapDom) => {
  new Map({
    target: mapDom,
    layers: [new TileLayer({ source: new OSM() })],
    view: new View({
      center: [114, 23], //广州坐标点
      maxZoom: 21,
      zoom: 8,
      minZoom: 5,
      projection: "EPSG:4326",
    }),
  });
};

/**
 * @use 初始化地图(需要添加地图图层)
 * @param mapDom 地图容器目标id
 */
export const initMap = (mapDom, config) => {
  overlay = createOverlay();
  map = new Map({
    overlays: [overlay],
    target: mapDom,
    view: view,
  });
  // 添加中国地图-高德地图
  const chinalayer = new TileLayer({
    source: new XYZ({
      url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}", //中国地图
    }),
  });
  map.addLayer(chinalayer);
  allLayers["china"] = chinalayer;

  // 添加世界地图图层
  const gaodeLayer = new TileLayers({
    source: new XYZ({
      url: "https://tile6.hifleet.com/wmts/google-street-zh/osm_grid/{z}/{x}/{y}.png", //世界地图
    }),
    projection: "EPSG:3857",
  });
  map.addLayer(gaodeLayer);
  allLayers["global"] = gaodeLayer;

  // 添加腾讯tms地图图层
  const tengxunTMSLayer = new TileLayers({
    source: new XYZ({
      url: "http://rt{0-3}.map.gtimg.com/realtimerender?x={x}&y={-y}&z={z}&type=vector&style=0", //腾讯tms地图
    }),
    projection: "EPSG:3857",
  });
  map.addLayer(tengxunTMSLayer);
  allLayers["tengxun"] = tengxunTMSLayer;

  // 添加航海地图图层
  const navigationLayer = new TileLayers({
    source: new XYZ({
      url: "http://m12.shipxy.com/tile.c?l=Na&m=o&x={x}&y={y}&z={z}", //航海地图
    }),
    projection: "EPSG:3857",
  });
  map.addLayer(navigationLayer);
  allLayers["navigation"] = navigationLayer;

  // 添加地图加载事件
  if (config.loading) {
    map.on("loadstart", function () {
      map.getTargetElement().classList.add("spinner");
    });
    map.on("loadend", function () {
      map.getTargetElement().classList.remove("spinner");
    });
  }

  // 添加地图单击弹出弹框事件
  if (config.pop) {
    map.on("singleclick", function (evt) {
      const coordinate = evt.coordinate;
      const content = document.getElementById("popup-content");
      content.innerHTML =
        "<p>当前点击位置经纬度：" + coordinate.toString() + "</p>";
      overlay.setPosition(coordinate);
    });
  }
};

/**
 * @use 实现热力图效果
 */
export const addHeatmapLayer = () => {
  const heatmapBlur = 10; // 模糊程度
  const heatmapRadius = 5; // 直径
  // 创建热力图图层
  const heatmapLayer = new HeatmapLayer({
    zIndex: 1,
    source: new VectorSource({}),
    blur: heatmapBlur,
    radius: heatmapRadius,
    weight: function () {
      const magnitude = 10; // 单个要素权重，越大越红
      return magnitude;
    },
  });
  map.addLayer(heatmapLayer);
  // 生成大批量的随机点位，正常业务中应该从后台接口获取数据
  const dataArr = [];
  for (let i = 0; i < 10000; i++) {
    const p = new Feature({
      geometry: new Point([Math.random() * 10 + 115, Math.random() * 5 + 30]),
    });
    dataArr.push(p);
  }
  // 将大批量的点添加到热力图图层，呈现热力图效果
  heatmapLayer.getSource().addFeatures(dataArr);
};

/**
 * @use (报错！！！) 使用webgl图层叠加超大量Point点要素，解决叠加超过一百万数据量点位导致浏览器卡住变慢的问
 */
export const addWebglLayer = () => {
  const webglIconStyle = {
    'symbol': {
      'symbolType': 'image',
      'src': cam_icon0,
      'size': [
        18,
        28
      ],
      'color': 'lightyellow',
      'rotateWithView': false,
      'offset': [
        0,
        9
      ]
    }
  }
  // 创建一个矢量图层，用于添加要素（多边形、圆形、线段）
  const webglLayer = new WebGLPointsLayer({
    zIndex: 2,
    source: new VectorSource({
      features: [],
    }),
    style: webglIconStyle,
    visible: true,
  });
  map.addLayer(webglLayer);
  // 生成大批量的点，正常业务中应该从后台接口获取数据
  const dataArr = [];
  for (let i = 0; i < 20000; i++) {
    const p = new Feature({
      geometry: new Point([Math.random() * 20 + 110, Math.random() * 10 + 30]),
    });
    dataArr.push(p);
  }
  // 将大批量的点要素添加到webgl图层
  webglLayer.getSource().addFeatures(dataArr);
};

/**
 * @use 添加一个Image图片点要素到地图上
 */
export const addImage = () => {
  // 样式
  const featureStyle = new Style({
    image: new Icon({
      src: cam_icon0,
      scale: 0.2, // 设置图片缩放级别，默认1（图片原尺寸）
    }),
  });

  const featureStyle1 = new Style({
    stroke: new Stroke({
      color: "green",
      width: 2,
    }),
    fill: new Fill({
      color: "pink",
    }),
    image: new CircleStyle({
      radius: 8,
      fill: new Fill({
        color: "blue",
      }),
      stroke: new Stroke({
        color: "white",
        width: 8,
      }),
    }),
  });

  console.log(featureStyle);
  // 创建一个矢量图层，用于添加要素（多边形、圆形、线段）
  const imageFeatureLayer = new VectorLayer({
    id: "eguid-marker",
    zIndex: 1,
    source: new VectorSource({
      features: [],
    }),
    style: featureStyle1,
    visible: true, // 是否显示图层，默认true
  });
  map.addLayer(imageFeatureLayer);
  // 点
  const point = new Feature({
    geometry: new Point([101.5, 31.5]),
  });
  // 点
  const point2 = new Feature({
    geometry: new Point([104.5, 31.5]),
  });
  // 线段
  const line = new Feature({
    geometry: new LineString([
      [101.5, 31.5],
      [104.5, 31.5],
    ]),
  });
  // 多边形
  const poly = new Feature({
    geometry: new Polygon([
      [
        [117, 31],
        [117.5, 32],
        [116, 32],
      ],
      [
        [106.5, 32.5],
        [107.5, 33.5],
        [108, 32.5],
        [106.5, 30],
        [105, 32.5],
        [105.5, 33.5],
      ],
    ]),
  });
  // 圆形
  const circle = new Feature({
    geometry: new Circle([90.5, 31.5], 5),
  });
  // console.log(circle);
  // 将要素添加到图层
  imageFeatureLayer
    .getSource()
    .addFeatures([point2, point, line, poly, circle]);
};

/**
 * @use 切换显示图层
 */
export const showLayer = (layerName) => {
  const allLas = Object.keys(allLayers);
  // 先隐藏所有地图图层
  for (const i of allLas) {
    allLayers[i].setVisible(false);
  }
  // 再显示某一个图层
  allLayers[layerName].setVisible(true);
};

/**
 * 创建地图弹框
 */
function createOverlay() {
  const container = document.getElementById("popup");
  const closer = document.getElementById("popup-closer");
  const overlay = new Overlay({
    element: container,
    autoPan: {
      animation: {
        duration: 250,
      },
    },
  });
  closer.onclick = function () {
    overlay.setPosition(undefined);
    closer.blur();
    return false;
  };
  return overlay;
}

/**
 * @use 添加高德地图图层
 */
export const addGaodeMap = () => {
  const gaodeLayer = new TileLayers({
    source: new XYZ({
      //   url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}",//中国地图
      url: "https://tile6.hifleet.com/wmts/google-street-zh/osm_grid/{z}/{x}/{y}.png", //世界地图
    }),
    projection: "EPSG:3857",
  });
  map.addLayer(gaodeLayer);
  allLayers["gaode"] = gaodeLayer;
};

/**
 * @use 添加离线地图
 */
export const addOutLineMap = () => {
  const gaodeLayer = new TileLayers({
    source: new XYZ({
      url: "http://192.168.0.106:8082" + "/{z}/{x}/{y}.jpg",
    }),
    projection: "EPSG:3857",
  });
  map.addLayer(gaodeLayer);
};

/**
 * @use 地图其他控件(缩放，滚动,鼠标所在经纬度坐标,全屏控件,网格标记线)
 * @param {*} otherControls
 */
export const otherControl = (otherControls) => {
  if (otherControls.scaleControl) {
    // 缩放控件
    const scaleControl = new control.ScaleLine();
    map.addControl(scaleControl);
  }

  if (otherControls.zoomSlider) {
    // 滚动条
    const zoomSlider = new control.ZoomSlider();
    map.addControl(zoomSlider);
  }

  if (otherControls.mousePosition) {
    // 显示鼠标所在经纬度坐标
    const mousePosition = new control.MousePosition();
    map.addControl(mousePosition);
  }
  if (otherControls.FullScreen) {
    // 全屏控件
    map.addControl(new control.FullScreen());
  }
  if (otherControls.addGraticuleLayer) {
    //地图全图显示经纬度网格标记线
    const graticuleLayer = new Graticule({
      strokeStyle: new Stroke({
        color: "rgba(255,30,30,0.8)",
        width: 2,
        lineDash: [0.5, 4],
      }),
      showLabels: true,
      wrapX: false,
    });
    map.addLayer(graticuleLayer);
  }

  if (otherControls.overviewMap) {
    //添加鹰眼控件
    const overviewMap = new control.OverviewMap({
      className: "ol-overviewmap ol-custom-overviewmap", //css类名(默认ol-overviewmap)
      collapsed: true, // 控件默认是否折叠（默认展开）
      collapseLabel: "\u00BB", // 用于展开的概览图按钮的文本标签。也可以使用元素（例如span元素）来代替文本。
      label: "\u00AB", // 用于折叠的概览图按钮的文本标签。也可以使用元素（例如元素）来代替文本。
      rotateWithView: false, // 控件视图是否应随主地图视图一起旋转(默认false)
      tipLabel: "Overview map", // 用于按钮提示的文本标签(默认'Overview map')
      layers: [
        new TileLayers({
          source: new XYZ({
            url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}",
          }),
        }),
      ],
    });
    map.addControl(overviewMap);
  }
};

/**
 * @use 调整中心点位置
 * @param x
 * @param y
 */
export const setCenter = (x, y) => {
  map.getView().setCenter([x, y]);
};

/**
 * @use 调整缩放级别
 * @param level
 */
export const setZoom = (level) => {
  map.getView().setZoom(level);
};

/**
 * @use 调整地图角度
 * @param rotate
 */
export const setRotation = (rotate) => {
  map.getView().setRotation(rotate);
};

/**
 * @use 地图复位
 */
export const resetMap = () => {
  map.getView().setCenter(defaultCenter);
  map.getView().setZoom(defaultZoom);
  map.getView().setRotation(defaultRotate);
};

/**
 * @use 地图单击事件
 * @param {*} cb 回调
 */
export const singleClickFun = (cb) => {
  map.on("singleclick", (e) => {
    // resetMap(); //复位可直接使用
    cb(e);
  });
};

/**
 * @use 根据多个点边界自动调整地图到
 * @param {@} points 坐标点二维数组
 */
export const fitToMultiPoint = (points) => {
  const featureStyle = new Style({
    stroke: new Stroke({
      color: "rgba(255,0,0, 1)",
      width: 2,
    }),
    fill: new Fill({
      color: "rgba(255,255,0, 0.5)",
    }),
    image: new CircleStyle({
      radius: 8,
      fill: new Fill({
        color: "blue",
      }),
      stroke: new Stroke({
        color: "white",
        width: 8,
      }),
    }),
  });
  // 创建一个矢量图层，用于添加要素（多边形、圆形、线段）
  const featureLayer = new VectorLayer({
    id: "eguid-marker",
    zIndex: 1,
    source: new VectorSource({
      features: [],
    }),
    style: featureStyle,
    visible: true, // 是否显示图层，默认true
  });
  map.addLayer(featureLayer);

  // 多边形
  const poly = new Feature({
    geometry: new Polygon([points]),
  });
  featureLayer.getSource().addFeature(poly);

  // 根据传进来的坐标创建一个矩形边界框
  const extent = boundingExtent(points);
  // 当前地图的可见区域
  const cuVisibleExtent = map.getView().calculateExtent(map.getSize());
  // 检查多个点的边界是否在可视区域内
  if (containsExtent(cuVisibleExtent, extent)) {
    // getCenter(extent) -> 获取到根据传进来的坐标创建一个矩形边界的中心点,并设置为中心点
    map.getView().setCenter(getCenter(extent));
    // console.log("当前多个点在当前可视区域范围内，直接修改中心点");
  } else {
    // 将当前地图可视区域缩放到多个点的边界
    map
      .getView()
      .fit(extent, { size: map.getSize(), padding: [100, 0, 100, 200] });
    // console.log("将当前地图可视区域缩放到多个点的边界");
  }
};

/**
 * @use 平移动画
 * @param {*} position 指定坐标点 [106.5, 29.5],
 * @param {*} duraion 动画持续时间
 */
export const panTo = (position, duraion) => {
  view.animate({
    center: position,
    duration: duraion != null ? duraion : 1500,
  });
};

/**
 * @use 飞行动画
 * @param location 位置
 * @param duration 动画持续时长
 * @param done 执行完毕回调
 */
export const flyTo = (location, duration, done) => {
  if (duration != null) {
    duration = 2000;
  }

  const zoom = view.getZoom();
  let parts = 2;
  let called = false;
  function callback(complete) {
    --parts;
    if (called) {
      return;
    }
    if (parts === 0 || !complete) {
      called = true;
      done(complete);
    }
  }
  view.animate(
    {
      center: location,
      duration: duration,
    },
    callback
  );
  view.animate(
    {
      zoom: zoom - 1,
      duration: duration / 2,
    },
    {
      zoom: zoom,
      duration: duration / 2,
    },
    callback
  );
};

/**
 * @use 旋转动画
 * @param position 指定坐标点 [106.5, 29.5],
 */
export const spinTo = (position) => {
  const center = view.getCenter();
  view.animate(
    {
      center: [
        center[0] + (position[0] - center[0]) / 2, //以要跳转的位置和当前位置的中间点作为旋转的中心点
        center[1] + (position[1] - center[1]) / 2,
      ],
      rotation: Math.PI,
      easing: easeIn,
    },
    {
      center: position,
      rotation: 2 * Math.PI,
      easing: easeOut,
    }
  );
};

/**
 * @use 弹簧跳跃动画
 * @param position 指定坐标点 [106.5, 29.5],
 */
export const elasticTo = (position, duraion) => {
  view.animate({
    center: position,
    duration: duraion != null ? duraion : 2000,
    easing: _elastic,
  });
};
// 计算弹簧跳跃动画
function _elastic(t) {
  return (
    Math.pow(2, -10 * t) * Math.sin(((t - 0.075) * (2 * Math.PI)) / 0.3) + 1
  );
}

// 计算弹跳动画
function _bounce(t) {
  const s = 7.5625;
  const p = 2.75;
  let l;
  if (t < 1 / p) {
    l = s * t * t;
  } else {
    if (t < 2 / p) {
      t -= 1.5 / p;
      l = s * t * t + 0.75;
    } else {
      if (t < 2.5 / p) {
        t -= 2.25 / p;
        l = s * t * t + 0.9375;
      } else {
        t -= 2.625 / p;
        l = s * t * t + 0.984375;
      }
    }
  }
  return l;
}

/**
 * @use 弹跳动画
 * @param position 指定坐标点 [106.5, 29.5],
 */
export const bounceTo = (position, duration) => {
  view.animate({
    center: position,
    duration: duration != null ? duration : 2000,
    easing: _bounce,
  });
};

//高亮显示经过的要素
const _displayFeatureInfo = (pixel) => {
  console.log("pixel", pixel);

  // 默认样式
  const style = new Style({
    fill: new Fill({
      color: "#eeeeee",
    }),
  });
  // 加载geojson数据并显示的图层
  const vectorLayer = new VectorLayer({
    background: "#1a2b39",
    source: new VectorSource({
      url: "https://openlayers.org/data/vector/ecoregions.json",
      format: new GeoJSON(),
    }),
    style: function (feature) {
      const color = feature.get("COLOR_NNH") || "#eeeeee";
      style.getFill().setColor(color);
      return style;
    },
  });
  map.addLayer(vectorLayer);

  let highlight;
  // 高亮样式
  const highlightStyle = new Style({
    stroke: new Stroke({
      color: "rgba(255, 255, 255, 0.8)",
      width: 5,
    }),
  });
  // 高亮显示图层
  const featureOverlayLayer = new VectorLayer({
    source: new VectorSource(),
    style: highlightStyle,
  });
  map.addLayer(featureOverlayLayer);
  //从图层中获取指定像素位置附近的要素
  vectorLayer.getFeatures(pixel).then((features) => {
    console.log("features", features);
    console.log("highlight", highlight);
    const feature = features.length ? features[0] : undefined;
    if (feature !== highlight) {
      if (highlight) {
        featureOverlayLayer.getSource().removeFeature(highlight); //删除高亮要素
      }
      if (feature) {
        featureOverlayLayer.getSource().addFeature(feature); //添加高亮要素到高亮图层
      }
      highlight = feature;
    }
  });
};

/**
 * @use 鼠标移过事件
 */
export const pointerMove = () => {
  //箭头鼠标移动事件
  map.on("pointermove", function (evt) {
    console.log("鼠标移过");
    if (evt.dragging) {
      //排除拖拽事件
      return;
    }
    const pixel = map.getEventPixel(evt.originalEvent); // 获取事件的像素位置
    _displayFeatureInfo(pixel);
  });
};
